package com.unitd.frame.comm.utils.encrypt;

import com.unitd.frame.comm.utils.CharsetUtils;

import java.security.MessageDigest;

/**
 * @author Hudan
 * @version 1.0
 * @desc MD5加解密工具类
 * @filename MD5Utils.java
 * @copyright www.unitd.com
 * @date 2016/11/13
 */
public class MD5Utils {

	/** MD5加密需要进行混淆的盐值 */
	private static final char[] HEXDIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	/** MD5加密盐值 */
	private static final char[] saltChars = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./".toCharArray());


	/**
	 * @desc 根据需要加密的字符串和盐值,将该字符串按照MD5算法进行加密
	 * @param str 需要进行MD5加密的字符串
	 * @param salt 加密盐值(放在需要加密的字符串末尾)
	 * @return MD5加密后的密文
	 */
	public static String encrypt(String str, String salt) {
		try {
			str = str.concat(salt);
			return encrypt(str.getBytes(CharsetUtils.UTF_8));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @desc 将字符串按照MD5算法进行加密
	 * @param str 需要进行MD5加密的字符串
	 * @return MD5加密后的密文
	 */
	public static String encrypt(String str) {
		try {
			return encrypt(str.getBytes(CharsetUtils.UTF_8));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @desc 将字节数组按照MD5算法进行加密
	 * @param bytes 需要进行MD5加密的字节数组
	 * @return MD5加密后的密文
	 */
	public static String encrypt(byte[] bytes) {
		try {
			// 获得MD5摘要算法的 MessageDigest 对象
			MessageDigest mdInst = MessageDigest.getInstance("MD5");
			// 使用指定的字节更新摘要
			mdInst.update(bytes);
			// 获得密文
			byte[] md = mdInst.digest();

			// 把密文转换成十六进制的字符串形式
			int j = md.length;
			char[] str = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				str[k++] = HEXDIGITS[byte0 >>> 4 & 0xf];
				str[k++] = HEXDIGITS[byte0 & 0xf];
			}
			return new String(str);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @desc 生成MD5短码(6位)
	 * @param orig 需要进行加密的原始串
	 * @return String
	 */
	public static String encryptShort(String orig) {
		if (orig == null) {
			return null;
		}
		// 先得到url的32个字符的md5码
		String md5 = encrypt(orig);
		// 将32个字符的md5码分成4段处理,每段8个字符, 段任意一段加密都可以,这里我们取第二段
		int offset = 1 * 8;
		String sub = md5.substring(offset, offset + 8);
		long sub16 = Long.parseLong(sub, 16); //将sub当作一个16进制的数,转成long
		// & 0X3FFFFFFF,去掉最前面的2位,只留下30位
		sub16 &= 0X3FFFFFFF;

		StringBuilder sb = new StringBuilder();
		//将剩下的30位分6段处理,每段5位
		for (int j = 0; j < 6; j++) {
			//得到一个 <= 61的数字
			long t = sub16 & 0x0000003D;
			sb.append(saltChars[(int) t]);
			sub16 >>= 5;  //将sub16右移5位

		}
		return sb.toString();
	}

	public static void main(String[] args) {
		System.out.println(MD5Utils.encrypt("loghub123xxx!!!"));
	}
}